package com.itcast.dao.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;
import javax.persistence.metamodel.EntityType;

import org.springframework.stereotype.Repository;

import com.itcast.dao.BaseDao;
import com.itcast.util.GenericsUtils;
import com.itcast.util.Dao.DaoUtils;
import com.itcast.util.Dao.OrderBy;
import com.itcast.util.Dao.PredicateDate;
import com.itcast.util.Dao.PredicateType;

@Repository
public abstract class BaseDaoImpl<T, PK extends Serializable> implements
		BaseDao<T, PK> {

	@PersistenceContext
	protected EntityManager entityManager;
	private Class<T> entityClass;

	public BaseDaoImpl() {
		entityClass = GenericsUtils.getSuperClassGenricType(getClass());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.itcast.dao.impl.BaseDao#add(T)
	 */
	public void add(T entity) {
		entityManager.persist(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.itcast.dao.impl.BaseDao#delete(T)
	 */
	public void delete(T entity) {
		entityManager.remove(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.itcast.dao.impl.BaseDao#deleteById(PK)
	 */
	public void deleteById(PK id) {
		entityManager.remove(entityManager.getReference(entityClass, id));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.itcast.dao.impl.BaseDao#update(T)
	 */
	public void update(T entity) {
		entityManager.merge(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.itcast.dao.impl.BaseDao#findById(PK)
	 */
	public T findById(PK id) {
		return entityManager.find(entityClass, id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.itcast.dao.impl.BaseDao#findList(int, int)
	 */
	public List<T> findList(int firstResult, int maxResults) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<T> criteriaQuery = criteriaBuilder
				.createQuery(entityClass);
		Root<T> entity = criteriaQuery.from(entityClass);
		criteriaQuery.select(entity);
		return entityManager.createQuery(criteriaQuery).setFirstResult(
				firstResult).setMaxResults(maxResults).getResultList();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.itcast.dao.impl.BaseDao#findListForWhere(java.util.LinkedHashMap,
	 * int, int)
	 */
	public List<T> findListForWhere(
			LinkedHashMap<PredicateDate, PredicateType> Predicates,
			int firstResult, int maxResults) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<T> criteriaQuery = criteriaBuilder
				.createQuery(entityClass);
		Root<T> entity = criteriaQuery.from(entityClass);
		criteriaQuery.select(entity).where(
				DaoUtils.getPredicateList(Predicates, criteriaBuilder, entity)
						.toArray(new Predicate[0]));
		return entityManager.createQuery(criteriaQuery).setFirstResult(
				firstResult).setMaxResults(maxResults).getResultList();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.itcast.dao.impl.BaseDao#findListAndOrderBy(java.util.LinkedHashMap,
	 * int, int)
	 */
	public List<T> findListAndOrderBy(LinkedHashMap<String, OrderBy> orders,
			int firstResult, int maxResults) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<T> criteriaQuery = criteriaBuilder
				.createQuery(entityClass);
		Root<T> entity = criteriaQuery.from(entityClass);
		criteriaQuery.select(entity).orderBy(
				DaoUtils.getOrderList(orders, criteriaBuilder, entity));
		return entityManager.createQuery(criteriaQuery).setFirstResult(
				firstResult).setMaxResults(maxResults).getResultList();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seecom.itcast.dao.impl.BaseDao#findListForWhereAndOrderBy(java.util.
	 * LinkedHashMap, java.util.LinkedHashMap, int, int)
	 */
	public List<T> findListForWhereAndOrderBy(
			LinkedHashMap<PredicateDate, PredicateType> Predicates,
			LinkedHashMap<String, OrderBy> orders, int firstResult,
			int maxResults) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<T> criteriaQuery = criteriaBuilder
				.createQuery(entityClass);
		Root<T> entity = criteriaQuery.from(entityClass);
		criteriaQuery.select(entity).where(
				DaoUtils.getPredicateList(Predicates, criteriaBuilder, entity)
						.toArray(new Predicate[0])).orderBy(
				DaoUtils.getOrderList(orders, criteriaBuilder, entity));
		return entityManager.createQuery(criteriaQuery).setFirstResult(
				firstResult).setMaxResults(maxResults).getResultList();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.itcast.dao.impl.BaseDao#findListByJpql(java.lang.String, int,
	 * int)
	 */
	public List<T> findListByJpql(String jpql, int firstResult, int maxResults) {
		TypedQuery<T> query = entityManager.createQuery(jpql, entityClass);
		return query.setFirstResult(firstResult).setMaxResults(maxResults)
				.getResultList();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.itcast.dao.impl.BaseDao#findListByJpql(java.lang.String,
	 * java.lang.Object[], int, int)
	 */
	public List<T> findListByJpql(String jpql, Object[] values,
			int firstResult, int maxResults) {
		TypedQuery<T> query = entityManager.createQuery(jpql, entityClass);
		for (int i = 0; i < values.length; i++) {
			Object value = values[i];
			query.setParameter(i + 1, value);
		}
		return query.setFirstResult(firstResult).setMaxResults(maxResults)
				.getResultList();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.itcast.dao.impl.BaseDao#findAll()
	 */
	public List<T> findAll() {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<T> criteriaQuery = criteriaBuilder
				.createQuery(entityClass);
		Root<T> entity = criteriaQuery.from(entityClass);
		criteriaQuery.select(entity);
		return entityManager.createQuery(criteriaQuery).getResultList();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.itcast.dao.impl.BaseDao#findAllForWhere(java.util.LinkedHashMap)
	 */
	public List<T> findAllForWhere(
			LinkedHashMap<PredicateDate, PredicateType> Predicates) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<T> criteriaQuery = criteriaBuilder
				.createQuery(entityClass);
		Root<T> entity = criteriaQuery.from(entityClass);
		criteriaQuery.select(entity).where(
				DaoUtils.getPredicateList(Predicates, criteriaBuilder, entity)
						.toArray(new Predicate[0]));
		return entityManager.createQuery(criteriaQuery).getResultList();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.itcast.dao.impl.BaseDao#findAllAndOrderBy(java.util.LinkedHashMap)
	 */
	public List<T> findAllAndOrderBy(LinkedHashMap<String, OrderBy> orders) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<T> criteriaQuery = criteriaBuilder
				.createQuery(entityClass);
		Root<T> entity = criteriaQuery.from(entityClass);
		criteriaQuery.select(entity).orderBy(
				DaoUtils.getOrderList(orders, criteriaBuilder, entity));
		return entityManager.createQuery(criteriaQuery).getResultList();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.itcast.dao.impl.BaseDao#findAllForWhereAndOrderBy(java.util.LinkedHashMap
	 * , java.util.LinkedHashMap, int, int)
	 */
	public List<T> findAllForWhereAndOrderBy(
			LinkedHashMap<PredicateDate, PredicateType> Predicates,
			LinkedHashMap<String, OrderBy> orders, int firstResult,
			int maxResults) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<T> criteriaQuery = criteriaBuilder
				.createQuery(entityClass);
		Root<T> entity = criteriaQuery.from(entityClass);
		criteriaQuery.select(entity).where(
				DaoUtils.getPredicateList(Predicates, criteriaBuilder, entity)
						.toArray(new Predicate[0])).orderBy(
				DaoUtils.getOrderList(orders, criteriaBuilder, entity));
		return entityManager.createQuery(criteriaQuery).getResultList();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.itcast.dao.impl.BaseDao#findAllByJpql(java.lang.String)
	 */
	public List<T> findAllByJpql(String jpql) {
		TypedQuery<T> query = entityManager.createQuery(jpql, entityClass);
		return query.getResultList();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.itcast.dao.impl.BaseDao#findAllByJpql(java.lang.String,
	 * java.lang.Object[])
	 */
	public List<T> findAllByJpql(String jpql, Object[] values) {
		TypedQuery<T> query = entityManager.createQuery(jpql, entityClass);
		for (int i = 0; i < values.length; i++) {
			Object value = values[i];
			query.setParameter(i + 1, value);
		}
		return query.getResultList();
	}
	
	public int findList(
			LinkedHashMap<PredicateDate, PredicateType> Predicates,
			int firstResult,
			int maxResults) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Object> criteriaQuery = criteriaBuilder
				.createQuery(Object.class);
		Root<T> entity = criteriaQuery.from(entityClass);
		EntityType<T> entity_ = entity.getModel();
		//List<Selection<Integer>> sa = new ArrayList<Selection<Integer>>();
		//sa.add(entity.<Integer>get("name"));
		criteriaQuery.select(criteriaBuilder.count(entity));
		//criteriaQuery.multiselect(entity.get(entity_.getSingularAttribute("name", String.class)));
		int string = Integer.parseInt(entityManager.createQuery(criteriaQuery).setFirstResult(
				firstResult).setMaxResults(maxResults).getSingleResult().toString());
		return string;
		//return entityManager.createQuery(criteriaQuery).setFirstResult(
		//		firstResult).setMaxResults(maxResults).getResultList();
	}
	

}
